/*
    IceCode toolkit - config
    Copyright (C) 2011 Alfredo 'IceCoder' Mungo

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <iostream>
#include <fstream>
#include <stdexcept>
#include <algorithm>
#include <string>
#include "config.hpp"

namespace icecode {
  namespace config {

    Configuration::Configuration(std::string cfg_path)
    {
      std::ifstream f(cfg_path);
      std::string s;
      unsigned int line = 0;

      if(f.is_open())
	{
	  do
	    {
	      std::getline(f, s);
	      line++;

	      if(!s.empty()) //Not a blank line
		if(s[0] != '#') //Not a comment
		  {
		    size_t pivot = s.find_first_of(':');

		    if(pivot == std::string::npos) //Invalid line, dump error and skip it.
		      {
		        std::cerr << "Invalid configuration line (" << line << ")." << std::endl;
			continue;
		      }

		    std::string name = s.substr(0, pivot), content = s.substr(pivot + 1);

		    {
		      //Inline comment deletion
		      size_t p = content.find('#');
		      while(p != std::string::npos)
			{
			  if(p > 0)
			    {
			      if(content[p - 1] != '\\') //Not escaped
				content.erase(p);
			      else
				{
				  content.erase(p - 1, 1);
				  p--;
				}
			    }
			  else
			    content.clear();
			  p = content.find('#', p + 1);
			}

		      //Trim spaces
		      p = name.find_first_not_of(' ');
		      if(p != 0 && p != std::string::npos)
			name.erase(0, p);

		      p = name.find_last_not_of(' ');
		      if(p != 0 && p != std::string::npos)
			name.erase(p + 1);

		      p = content.find_first_not_of(' ');
		      if(p != 0 && p != std::string::npos)
			content.erase(0, p);

		      p = content.find_last_not_of(' ');
		      if(p != 0 && p != std::string::npos)
			content.erase(p + 1);

		      //Trim tabs
		      p = name.find_first_not_of('\t');
		      if(p != 0 && p != std::string::npos)
			name.erase(0, p);

		      p = name.find_last_not_of('\t');
		      if(p != 0 && p != std::string::npos)
			name.erase(p + 1);

		      p = content.find_first_not_of('\t');
		      if(p != 0 && p != std::string::npos)
			content.erase(0, p);

		      p = content.find_last_not_of('\t');
		      if(p != 0 && p != std::string::npos)
			content.erase(p + 1);
		    }

		    vars.insert(var(name, content));
		  }
	    } while(f.eof() == false);

	  f.close();

	}
      else throw std::invalid_argument("File not found.");
    }

    std::string& Configuration::operator [](std::string var_name)
    {
      return vars[var_name];
    }

    bool Configuration::save(std::string cfg_path)
    {
      if(cfg_path.empty())
	cfg_path = path;

      if(cfg_path.empty())
	throw std::invalid_argument("Invalid filename.");

      std::ofstream f(cfg_path);

      if(f.is_open())
	{
	  std::for_each(vars.cbegin(), vars.cend(), [&](var s) { if(!s.second.empty()) f << s.first << ": " << s.second << std::endl; });

	  f.close();
	  return true;
	}
      else
	return false;
    }
  }
}
